package com.hxkj.controller;

import com.hxkj.client.service.MessageClient;
import com.hxkj.client.service.NormalNotificationClient;
import com.hxkj.constant.Constants;
import com.hxkj.domain.enums.UserExceptionEnum;
import com.hxkj.domain.po.CommonMessage;
import com.hxkj.domain.po.NormalEmail;
import com.hxkj.domain.po.SysUser;
import com.hxkj.domain.result.RequestParameterDataStrcut;
import com.hxkj.domain.result.UserParameterDataStruct;
import com.hxkj.service.UserService;
import com.hxkj.utils.ValidateCodeUtil;
import com.platform.common.domain.exception.CustomException;
import com.platform.common.domain.result.ResultDataStruct;
import com.platform.common.domain.result.ResultMessageStruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * created by kezhijie on 2018/8/21 17:28.
 * email to kezhijie@hxoss.com
 */
@RestController
@RequestMapping("/validatecode")
@Slf4j
public class ValidateCodeController {

    @Autowired
    private MessageClient messageClient;

    @Autowired
    private ValidateCodeUtil validateCodeUtil;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private NormalNotificationClient normalNotificationClient;

    private String phoneRegex = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";// 验证手机号

    private Pattern phone = Pattern.compile(phoneRegex);

    /**
     * 获取页面验证码
     * @param response
     * @param request
     * @throws Exception
     */
    @GetMapping(value = "getcode")
    public void valicode(HttpServletResponse response, HttpServletRequest request) throws  Exception {
        validateCodeUtil.generateVerificationCode(response, request);
    }

    /**
     * 1.手机注册时发送验证码（不会检查用户是否已存在），与手机注册接口（registerByPhone）配套使用
     * 2.未有手机号绑定手机号时发送验证码，与修改手机号接口(updatePhone)配套使用
     * @param sysUser
     * @return
     */
    @PostMapping(value = "registercode")
    public Callable<ResultMessageStruct> registercode(@RequestBody SysUser sysUser) {
        return () -> {
            ResultMessageStruct resultMessageStruct = new ResultMessageStruct();
            try {
                generateDynamicCode(sysUser, Constants.REDIS_PREFIX_TELEPHONE_LOGIN);
                resultMessageStruct.setCode(200);
                resultMessageStruct.setMessage("成功");
            } catch (CustomException e) {
                resultMessageStruct.setCode(e.getCode().getCode());
                resultMessageStruct.setMessage(e.getMessage());
            } catch (Exception e) {
                resultMessageStruct.setCode(500);
                resultMessageStruct.setMessage(e.getMessage());
            }
            return resultMessageStruct;
        };
    }

    /**
     * 获取短信动态码（会检查用户是否已注册）
     * @param sysUser
     * @throws Exception
     */
    @PostMapping(value = "sendcode")
    public Callable<ResultMessageStruct> mobilecode(@RequestBody SysUser sysUser) throws  Exception {
        return () -> {
            ResultMessageStruct resultMessageStruct = new ResultMessageStruct();
            try {
                // 检查用户是否已注册
                SysUser existsSysUser = userService.existsUser(sysUser);
                if (Objects.isNull(existsSysUser))
                    throw new CustomException(UserExceptionEnum.USER_NOT_EXIST);

                generateDynamicCode(sysUser, Constants.REDIS_PREFIX_TELEPHONE_LOGIN);
                resultMessageStruct.setCode(200);
                resultMessageStruct.setMessage("成功");
            } catch (CustomException e) {
                resultMessageStruct.setCode(e.getCode().getCode());
                resultMessageStruct.setMessage(e.getMessage());
            } catch (Exception e) {
                resultMessageStruct.setCode(500);
                resultMessageStruct.setMessage(e.getMessage());
            }
            return resultMessageStruct;
        };
    }

    /**
     * 接口用处：
     * 1.找回密码通过手机验证码，与verifyCode接口配套使用
     * 2.修改手机号验证已有手机号，与verifyCode接口配套使用
     * @param sysUser
     * @throws Exception
     */
    @PostMapping(value = "redeemByPhone")
    public Callable<ResultMessageStruct> redeemByPhone(@RequestBody SysUser sysUser) throws Exception {
        return () -> sendMessage(sysUser);
    }

    /**
     * 发送手机短信
     * @param sysUser
     * @return
     */
    private ResultMessageStruct sendMessage(SysUser sysUser) {
        ResultMessageStruct resultMessageStruct = new ResultMessageStruct();
        try {
            // 检查用户是否已注册
            SysUser existsSysUser = userService.existsUser(sysUser);
            if (Objects.isNull(existsSysUser))
                throw new CustomException(UserExceptionEnum.USER_NOT_EXIST);

            generateDynamicCode(existsSysUser, Constants.REDIS_PREFIX_REDEEM_BY_PHONE);

            resultMessageStruct.setCode(200);
            resultMessageStruct.setMessage("成功，已往手机发送验证码");
        } catch (CustomException e) {
            resultMessageStruct.setCode(e.getCode().getCode());
            resultMessageStruct.setMessage(e.getMessage());
        } catch (Exception e) {
            resultMessageStruct.setCode(500);
            resultMessageStruct.setMessage(e.getMessage());
        }

        return resultMessageStruct;
    }

    /**
     * 生成动态码
     * @param sysUser 主要找里面的phone
     * @param storePrefix redis存储前缀， {@link Constants}
     * @throws Exception
     */
    private void generateDynamicCode(SysUser sysUser, String storePrefix) throws  Exception {
        // 检查填入信息
        if (Objects.isNull(sysUser) || Objects.isNull(sysUser.getPhone()))
            throw new CustomException(UserExceptionEnum.USER_PHONE_IS_NULL);

        // 生成验证码
        int dynamicCode =  (int)((Math.random()*9+1)*100000);

        // 区分忘记密码找回和动态码登录
        String storeKey = storePrefix + sysUser.getPhone();

        redisTemplate.boundValueOps(storeKey).set(dynamicCode);
        redisTemplate.boundValueOps(storeKey).expire(5, TimeUnit.MINUTES);

        // 生成短信
        CommonMessage commonMessage = CommonMessage.builder()
                .messageType("登录")
                .message(String.valueOf(dynamicCode))
                .telephone(sysUser.getPhone())
                .serviceId("cms-user-center")
                .build();

        ResultMessageStruct resultMessageStruct = messageClient.sendMessage(commonMessage);

        log.info(resultMessageStruct.toString());
        if (resultMessageStruct.getCode() == 100)
            throw new CustomException(UserExceptionEnum.SEND_MESSAGE_PARAM_ERROR);
        else if (resultMessageStruct.getCode() == 101)
            throw new CustomException(UserExceptionEnum.SEND_MESSAGE_REQUEST_LIMIT);
        else if (resultMessageStruct.getCode() == 102)
            throw new CustomException(UserExceptionEnum.SEND_MESSAGE_SEND_LIMIT);
    }

    /**
     * 找回密码发送邮箱验证码
     * @param sysUser 用户基本信息
     * @throws Exception
     */
    @PostMapping(value = "redeemByEmail")
    public Callable<ResultMessageStruct> redeemByEmail(@RequestBody SysUser sysUser) throws Exception {
        return () -> sendEmail(sysUser);

    }

    /**
     * 发送邮件
     * @param sysUser
     * @return
     */
    private ResultMessageStruct sendEmail(SysUser sysUser) {
        ResultMessageStruct resultMessageStruct = new ResultMessageStruct();

        try {
            if (Objects.isNull(sysUser) || Objects.isNull(sysUser.getEmail()))
                throw new CustomException(UserExceptionEnum.USER_EMAIL_IS_NULL);

            // 检查用户是否已注册
            SysUser existsSysUser = userService.existsUser(sysUser);
            if (Objects.isNull(existsSysUser))
                throw new CustomException(UserExceptionEnum.USER_NOT_EXIST);

            // 存储redis的key
            String storeKey = Constants.REDIS_PREFIX_REDEEM_BY_EMAIL + sysUser.getEmail();

            // 生成验证码
            int dynamicCode = (int) ((Math.random() * 9 + 1) * 100000);

            redisTemplate.boundValueOps(storeKey).set(dynamicCode);
            redisTemplate.boundValueOps(storeKey).expire(5, TimeUnit.MINUTES);

            NormalEmail normalEmail = new NormalEmail();
            normalEmail.setTopic("【中亿财经网】邮箱验证");
            normalEmail.setMsg("验证码：" + dynamicCode);
            normalEmail.setReceiver(sysUser.getEmail());

            resultMessageStruct = normalNotificationClient.sendEmail(normalEmail);

        } catch (CustomException e) {

            resultMessageStruct.setCode(e.getCode().getCode());
            resultMessageStruct.setMessage(e.getMessage());

        } catch (Exception e) {
            resultMessageStruct.setCode(500);
            resultMessageStruct.setMessage(e.getMessage());
        }

        return resultMessageStruct;
    }

    /**
     * 验证手机/邮箱验证码
     * 与redeemByPhone或redeemByEmail配套使用
     * @param requestParameterDataStrcut  type: 1: 手机号， 2：邮箱
     * @throws Exception
     */
    @PostMapping(value = "verifyCode")
    public Callable<ResultMessageStruct> verifyEmailCode(@RequestBody RequestParameterDataStrcut<SysUser, String, String> requestParameterDataStrcut) throws Exception {
        return () -> {
            ResultMessageStruct resultMessageStruct = new ResultMessageStruct();
            SysUser sysUser = requestParameterDataStrcut.getData();
            String code = requestParameterDataStrcut.getCode();
            String type = requestParameterDataStrcut.getRequest();

            try {

                if (StringUtils.isEmpty(code))
                    throw new CustomException(UserExceptionEnum.REQUIRE_VALIDATECODE);

                // 验证基本信息
                if (Objects.isNull(sysUser) || (Objects.isNull(sysUser.getPhone()) && type.equals("1")))
                    throw new CustomException(UserExceptionEnum.USER_EMAIL_IS_NULL);
                if (Objects.isNull(sysUser) || (Objects.isNull(sysUser.getEmail()) && type.equals("2")))
                    throw new CustomException(UserExceptionEnum.USER_EMAIL_IS_NULL);

                // 组成redis key
                String storeKey;
                if (type.equals("1"))
                    storeKey = Constants.REDIS_PREFIX_REDEEM_BY_PHONE + sysUser.getPhone();
                else
                    storeKey = Constants.REDIS_PREFIX_REDEEM_BY_EMAIL + sysUser.getEmail();


                // 验证验证码
                if (Objects.isNull(redisTemplate.boundValueOps(storeKey).get()))
                    throw new CustomException(UserExceptionEnum.VALIDATECODE_INVALID);

                String originCode = redisTemplate.boundValueOps(storeKey).get().toString();

                if (!originCode.equalsIgnoreCase(code))
                    throw new CustomException(UserExceptionEnum.VALIDATECODE_IS_ERROR);

//                redisTemplate.delete(storeKey);

                resultMessageStruct.setCode(200);
                resultMessageStruct.setMessage("成功");
            } catch (CustomException e){

                resultMessageStruct.setCode(e.getCode().getCode());
                resultMessageStruct.setMessage(e.getMessage());

            } catch (Exception e) {
                resultMessageStruct.setCode(500);
                resultMessageStruct.setMessage(e.getMessage());
            }

            return resultMessageStruct;
        };

    }

    /**
     * 这个接口使用之前，需要先使用验证码接口
     * 验证码接口：com.hxkj.controller.ValidateCodeController#valicode(javax.servlet.http.HttpServletResponse, javax.servlet.http.HttpServletRequest)
     * @param sysUser
     * @throws Exception
     */
    @PostMapping(value = "verifyUser")
    public Callable<ResultMessageStruct> verifyUser(@RequestBody SysUser sysUser) throws Exception {
        return () -> {
            ResultMessageStruct resultMessageStruct = new ResultMessageStruct();

            try {
                SysUser existsSysUser = userService.existsUser(sysUser);
                if (Objects.isNull(existsSysUser))
                    throw new CustomException(UserExceptionEnum.USER_NOT_EXIST);

                // 根据前端要求，验证身份成功后，立马发送验证码到手机或邮箱
                Matcher phoneMatch = phone.matcher(sysUser.getUserAccount());
                if (phoneMatch.matches()) {
                    sendMessage(sysUser);
                } else {
                    sendEmail(sysUser);
                }

                resultMessageStruct.setCode(200);
                resultMessageStruct.setMessage("成功，已发送验证码到邮箱");
            } catch (CustomException e) {

                resultMessageStruct.setCode(e.getCode().getCode());
                resultMessageStruct.setMessage(e.getMessage());

            } catch (Exception e) {
                resultMessageStruct.setCode(500);
                resultMessageStruct.setMessage(e.getMessage());
            }

            return resultMessageStruct;
        };
    }

    @PostMapping("sendNewEmailVaildateCode")
    public Callable<ResultMessageStruct> sendNewEmailVaildateCode(@RequestBody String newEmail) {
        return () -> {
            ResultMessageStruct resultMessageStruct = new ResultMessageStruct();

            try {

                String storeKey = Constants.REDIS_PREFIX_MODIFY_NEW_EMAIL + newEmail;

                // 生成验证码
                int dynamicCode = (int) ((Math.random() * 9 + 1) * 100000);

                redisTemplate.boundValueOps(storeKey).set(dynamicCode);
                redisTemplate.boundValueOps(storeKey).expire(5, TimeUnit.MINUTES);

                NormalEmail normalEmail = new NormalEmail();
                normalEmail.setTopic("【中亿财经网】验证新邮箱");
                normalEmail.setMsg("验证码：" + dynamicCode);
                normalEmail.setReceiver(newEmail);

                resultMessageStruct = normalNotificationClient.sendEmail(normalEmail);

                resultMessageStruct.setCode(200);
                resultMessageStruct.setMessage("发送验证码成功");

            } catch (Exception e) {
                resultMessageStruct.setCode(500);
                resultMessageStruct.setMessage(e.getMessage());
            }

            return resultMessageStruct;
        };
    }

    @PostMapping("modifyNewEmail")
    public Callable<ResultMessageStruct> modifyNewEmail(@RequestBody RequestParameterDataStrcut<String, String, String> requestParameterDataStrcut) {
        return () -> {
            ResultMessageStruct resultMessageStruct = new ResultMessageStruct();

            String newEmail = requestParameterDataStrcut.getData();
            String code = requestParameterDataStrcut.getCode();
            String userId = requestParameterDataStrcut.getRequest();

            try {

                String storeKey = Constants.REDIS_PREFIX_MODIFY_NEW_EMAIL + newEmail;

                // 验证验证码
                if (Objects.isNull(redisTemplate.boundValueOps(storeKey).get()))
                    throw new CustomException(UserExceptionEnum.VALIDATECODE_INVALID);

                String originCode = redisTemplate.boundValueOps(storeKey).get().toString();

                if (!originCode.equalsIgnoreCase(code))
                    throw new CustomException(UserExceptionEnum.VALIDATECODE_IS_ERROR);

//                redisTemplate.delete(storeKey);

                SysUser sysUser = new SysUser();
                sysUser.setEmail(newEmail);
                sysUser.setUserId(userId);
                ResultDataStruct<SysUser> resultDataStruct = userService.update(sysUser);
                resultMessageStruct.setCode(resultDataStruct.getCode());
                resultMessageStruct.setMessage(resultDataStruct.getMessage());

            } catch (CustomException e) {
                resultMessageStruct.setCode(e.getCode().getCode());
                resultMessageStruct.setMessage(e.getMessage());
            } catch (Exception e) {
                log.error("修改信息异常：", e);
                resultMessageStruct.setCode(500);
                resultMessageStruct.setMessage(e.getMessage());
            }

            return resultMessageStruct;
        };
    }
}
